// 压缩/解压模块中文API封装
const fs = require('fs');
const path = require('path');

// 压缩库
let archiver;
try {
  archiver = require('archiver');
} catch (e) {
  console.warn('警告: 未安装 archiver 库，请运行 "npm install archiver" 来安装');
}

// 解压库
let unzipper;
try {
  unzipper = require('unzipper');
} catch (e) {
  console.warn('警告: 未安装 unzipper 库，请运行 "npm install unzipper" 来安装解压功能');
}

/**
 * 创建新的归档实例
 * @param {string} 格式 - 归档格式，如 'zip', 'tar'
 * @param {Object} 选项 - 归档选项
 * @returns {Object} 归档实例
 */
function 创建归档(格式 = 'zip', 选项 = {}) {
  if (!archiver) {
    throw new Error('未安装 archiver 库');
  }
  return archiver(格式, 选项);
}

/**
 * 创建并保存ZIP文件
 * @param {string} 输出文件路径 - ZIP文件保存路径
 * @param {Array} 文件列表 - 要添加的文件和目录列表
 * @param {Object} 选项 - 归档选项
 * @returns {Promise<void>}
 */
async function 创建ZIP文件(输出文件路径, 文件列表 = [], 选项 = {}) {
  return 创建归档文件('zip', 输出文件路径, 文件列表, 选项);
}

/**
 * 创建并保存TAR文件
 * @param {string} 输出文件路径 - TAR文件保存路径
 * @param {Array} 文件列表 - 要添加的文件和目录列表
 * @param {Object} 选项 - 归档选项
 * @returns {Promise<void>}
 */
async function 创建TAR文件(输出文件路径, 文件列表 = [], 选项 = {}) {
  return 创建归档文件('tar', 输出文件路径, 文件列表, 选项);
}

/**
 * 创建并保存归档文件
 * @param {string} 格式 - 归档格式
 * @param {string} 输出文件路径 - 输出文件保存路径
 * @param {Array} 文件列表 - 要添加的文件和目录列表
 * @param {Object} 选项 - 归档选项
 * @returns {Promise<void>}
 */
async function 创建归档文件(格式, 输出文件路径, 文件列表 = [], 选项 = {}) {
  if (!archiver) {
    throw new Error('未安装 archiver 库');
  }

  return new Promise((resolve, reject) => {
    // 确保输出目录存在
    const 输出目录 = path.dirname(输出文件路径);
    if (!fs.existsSync(输出目录)) {
      fs.mkdirSync(输出目录, { recursive: true });
    }

    // 创建写入流
    const 输出流 = fs.createWriteStream(输出文件路径);
    
    // 创建归档实例
    const 归档 = archiver(格式, 选项);

    // 监听错误事件
    归档.on('error', (错误) => {
      reject(new Error(`创建归档文件失败: ${错误.message}`));
    });

    // 监听完成事件
    输出流.on('close', () => {
      resolve();
    });

    // 管道连接
    归档.pipe(输出流);

    // 添加文件和目录
    文件列表.forEach((项目) => {
      if (typeof 项目 === 'string') {
        // 单个文件路径
        const 文件名 = path.basename(项目);
        if (fs.existsSync(项目)) {
          if (fs.statSync(项目).isDirectory()) {
            归档.directory(项目, 文件名);
          } else {
            归档.file(项目, { name: 文件名 });
          }
        }
      } else if (typeof 项目 === 'object' && 项目) {
        // 对象格式 {源路径, 目标路径}
        const { 源路径, 目标路径 } = 项目;
        if (源路径 && fs.existsSync(源路径)) {
          if (fs.statSync(源路径).isDirectory()) {
            归档.directory(源路径, 目标路径 || path.basename(源路径));
          } else {
            归档.file(源路径, { name: 目标路径 || path.basename(源路径) });
          }
        }
      }
    });

    // 完成归档
    归档.finalize();
  });
}

/**
 * 添加文件到归档
 * @param {Object} 归档实例 - archiver实例
 * @param {string} 文件路径 - 要添加的文件路径
 * @param {string} 目标路径 - 在归档中的目标路径（可选）
 * @returns {Object} 更新后的归档实例
 */
function 添加文件到归档(归档实例, 文件路径, 目标路径 = null) {
  if (!archiver) {
    throw new Error('未安装 archiver 库');
  }

  if (!fs.existsSync(文件路径)) {
    throw new Error(`文件不存在: ${文件路径}`);
  }

  归档实例.file(文件路径, { name: 目标路径 || path.basename(文件路径) });
  return 归档实例;
}

/**
 * 添加目录到归档
 * @param {Object} 归档实例 - archiver实例
 * @param {string} 目录路径 - 要添加的目录路径
 * @param {string} 目标路径 - 在归档中的目标路径（可选）
 * @returns {Object} 更新后的归档实例
 */
function 添加目录到归档(归档实例, 目录路径, 目标路径 = null) {
  if (!archiver) {
    throw new Error('未安装 archiver 库');
  }

  if (!fs.existsSync(目录路径)) {
    throw new Error(`目录不存在: ${目录路径}`);
  }

  归档实例.directory(目录路径, 目标路径 || path.basename(目录路径));
  return 归档实例;
}

/**
 * 添加缓冲区数据到归档
 * @param {Object} 归档实例 - archiver实例
 * @param {Buffer} 数据 - 要添加的数据
 * @param {string} 文件名 - 在归档中的文件名
 * @param {Object} 选项 - 文件选项
 * @returns {Object} 更新后的归档实例
 */
function 添加缓冲区到归档(归档实例, 数据, 文件名, 选项 = {}) {
  if (!archiver) {
    throw new Error('未安装 archiver 库');
  }

  归档实例.append(数据, { name: 文件名, ...选项 });
  return 归档实例;
}

/**
 * 添加文本到归档
 * @param {Object} 归档实例 - archiver实例
 * @param {string} 文本 - 要添加的文本内容
 * @param {string} 文件名 - 在归档中的文件名
 * @param {Object} 选项 - 文件选项
 * @returns {Object} 更新后的归档实例
 */
function 添加文本到归档(归档实例, 文本, 文件名, 选项 = {}) {
  if (!archiver) {
    throw new Error('未安装 archiver 库');
  }

  归档实例.append(文本, { name: 文件名, ...选项 });
  return 归档实例;
}

/**
 * 从流创建归档并保存
 * @param {string} 格式 - 归档格式
 * @param {string} 输出文件路径 - 输出文件保存路径
 * @param {Object} 流数据 - 包含流和文件名的对象 {流, 文件名}
 * @param {Object} 选项 - 归档选项
 * @returns {Promise<void>}
 */
async function 从流创建归档(格式, 输出文件路径, 流数据, 选项 = {}) {
  if (!archiver) {
    throw new Error('未安装 archiver 库');
  }

  return new Promise((resolve, reject) => {
    // 确保输出目录存在
    const 输出目录 = path.dirname(输出文件路径);
    if (!fs.existsSync(输出目录)) {
      fs.mkdirSync(输出目录, { recursive: true });
    }

    // 创建写入流
    const 输出流 = fs.createWriteStream(输出文件路径);
    
    // 创建归档实例
    const 归档 = archiver(格式, 选项);

    // 监听错误事件
    归档.on('error', (错误) => {
      reject(new Error(`从流创建归档失败: ${错误.message}`));
    });

    // 监听完成事件
    输出流.on('close', () => {
      resolve();
    });

    // 管道连接
    归档.pipe(输出流);

    // 添加流数据
    归档.append(流数据.流, { name: 流数据.文件名 });

    // 完成归档
    归档.finalize();
  });
}

/**
 * 设置归档的最大文件大小
 * @param {Object} 归档实例 - archiver实例
 * @param {number} 大小 - 最大文件大小（字节）
 * @returns {Object} 更新后的归档实例
 */
function 设置归档最大大小(归档实例, 大小) {
  if (!archiver) {
    throw new Error('未安装 archiver 库');
  }

  归档实例.setMaxListeners(0); // 移除最大监听器限制
  归档实例.on('progress', (进度) => {
    if (进度.fs.processedBytes > 大小) {
      throw new Error(`归档大小超过限制: ${大小} 字节`);
    }
  });
  
  return 归档实例;
}

/**
 * 获取归档进度
 * @param {Object} 归档实例 - archiver实例
 * @param {Function} 回调函数 - 进度回调函数
 * @returns {Object} 更新后的归档实例
 */
function 监听归档进度(归档实例, 回调函数) {
  if (!archiver) {
    throw new Error('未安装 archiver 库');
  }

  归档实例.on('progress', (进度) => {
    回调函数({
      条目总数: 进度.entries.total,
      已处理条目: 进度.entries.processed,
      已处理字节数: 进度.fs.processedBytes,
      总字节数: 进度.fs.totalBytes,
      百分比: 进度.fs.totalBytes > 0 ? (progress.fs.processedBytes / progress.fs.totalBytes * 100).toFixed(2) : '0'
    });
  });
  
  return 归档实例;
}

/**
 * 创建分割归档
 * @param {string} 格式 - 归档格式
 * @param {string} 输出文件路径前缀 - 输出文件路径前缀
 * @param {Array} 文件列表 - 要添加的文件和目录列表
 * @param {number} 分割大小 - 分割大小（字节）
 * @param {Object} 选项 - 归档选项
 * @returns {Promise<Array>} 生成的文件路径数组
 */
async function 创建分割归档(格式, 输出文件路径前缀, 文件列表 = [], 分割大小, 选项 = {}) {
  if (!archiver) {
    throw new Error('未安装 archiver 库');
  }

  return new Promise((resolve, reject) => {
    // 确保输出目录存在
    const 输出目录 = path.dirname(输出文件路径前缀);
    if (!fs.existsSync(输出目录)) {
      fs.mkdirSync(输出目录, { recursive: true });
    }

    // 创建分割归档实例
    const 归档 = archiver(格式, {
      ...选项,
      store: true // 分割归档需要禁用压缩
    });

    const 生成的文件 = [];
    let 当前文件索引 = 0;
    let 当前文件路径;
    let 当前输出流;

    // 创建新的输出文件
    function 创建新输出文件() {
      if (当前输出流) {
        当前输出流.end();
      }
      
      当前文件路径 = `${输出文件路径前缀}.${当前文件索引.toString().padStart(3, '0')}`;
      生成的文件.push(当前文件路径);
      当前输出流 = fs.createWriteStream(当前文件路径);
      归档.pipe(当前输出流);
      当前文件索引++;
    }

    // 初始化第一个文件
    创建新输出文件();

    // 监听错误事件
    归档.on('error', (错误) => {
      reject(new Error(`创建分割归档失败: ${错误.message}`));
    });

    // 监听数据事件，实现分割逻辑
    归档.on('data', (数据) => {
      const 当前文件状态 = fs.statSync(当前文件路径);
      if (当前文件状态.size > 分割大小) {
        归档.unpipe(当前输出流);
        创建新输出文件();
      }
    });

    // 监听完成事件
    归档.on('end', () => {
      if (当前输出流) {
        当前输出流.end();
      }
      resolve(生成的文件);
    });

    // 添加文件和目录
    文件列表.forEach((项目) => {
      if (typeof 项目 === 'string') {
        const 文件名 = path.basename(项目);
        if (fs.existsSync(项目)) {
          if (fs.statSync(项目).isDirectory()) {
            归档.directory(项目, 文件名);
          } else {
            归档.file(项目, { name: 文件名 });
          }
        }
      } else if (typeof 项目 === 'object' && 项目) {
        const { 源路径, 目标路径 } = 项目;
        if (源路径 && fs.existsSync(源路径)) {
          if (fs.statSync(源路径).isDirectory()) {
            归档.directory(源路径, 目标路径 || path.basename(源路径));
          } else {
            归档.file(源路径, { name: 目标路径 || path.basename(源路径) });
          }
        }
      }
    });

    // 完成归档
    归档.finalize();
  });
}

/**
 * 获取归档统计信息
 * @param {Object} 归档实例 - archiver实例
 * @returns {Object} 归档统计信息
 */
function 获取归档统计(归档实例) {
  if (!archiver) {
    throw new Error('未安装 archiver 库');
  }

  return {
    条目数: 归档实例.pointer(),
    字节数: 归档实例.pointer(),
    格式: 归档实例.options.format || 'unknown'
  };
}

/**
 * 流式输出归档到HTTP响应
 * @param {Object} 归档实例 - archiver实例
 * @param {Object} 响应对象 - Express/Fastify等响应对象
 * @param {string} 文件名 - 下载的文件名
 * @returns {Promise<void>}
 */
async function 流式输出归档到响应(归档实例, 响应对象, 文件名) {
  if (!archiver) {
    throw new Error('未安装 archiver 库');
  }

  return new Promise((resolve, reject) => {
    // 设置响应头
    响应对象.attachment(文件名);
    响应对象.setHeader('Content-Type', 'application/zip');

    // 监听错误事件
    归档实例.on('error', (错误) => {
      reject(new Error(`流式输出归档失败: ${错误.message}`));
    });

    // 监听完成事件
    归档实例.on('end', () => {
      resolve();
    });

    // 管道连接
    归档实例.pipe(响应对象);

    // 完成归档
    归档实例.finalize();
  });
}

/**
 * 验证unzipper库是否已安装
 * @returns {boolean} 库是否可用
 */
function 验证Unzipper库() {
  if (!unzipper) {
    throw new Error('未安装 unzipper 库，请运行 "npm install unzipper" 来安装');
  }
  return true;
}

/**
 * 解压ZIP文件到指定目录
 * @param {string} 压缩文件路径 - ZIP文件路径
 * @param {string} 目标目录 - 解压目标目录
 * @returns {Promise<void>}
 */
async function 解压ZIP文件(压缩文件路径, 目标目录) {
  验证Unzipper库();
  
  // 确保目标目录存在
  if (!fs.existsSync(目标目录)) {
    fs.mkdirSync(目标目录, { recursive: true });
  }
  
  return new Promise((resolve, reject) => {
    if (!fs.existsSync(压缩文件路径)) {
      reject(new Error(`压缩文件不存在: ${压缩文件路径}`));
      return;
    }
    
    fs.createReadStream(压缩文件路径)
      .pipe(unzipper.Extract({ path: 目标目录 }))
      .on('close', () => resolve())
      .on('error', (错误) => reject(new Error(`解压ZIP文件失败: ${错误.message}`)));
  });
}

/**
 * 解压ZIP文件中的特定文件
 * @param {string} 压缩文件路径 - ZIP文件路径
 * @param {string} 目标文件 - 要解压的目标文件路径
 * @param {string} 输出路径 - 输出文件路径
 * @returns {Promise<void>}
 */
async function 解压特定文件(压缩文件路径, 目标文件, 输出路径) {
  验证Unzipper库();
  
  return new Promise((resolve, reject) => {
    if (!fs.existsSync(压缩文件路径)) {
      reject(new Error(`压缩文件不存在: ${压缩文件路径}`));
      return;
    }
    
    // 确保输出目录存在
    const 输出目录 = path.dirname(输出路径);
    if (!fs.existsSync(输出目录)) {
      fs.mkdirSync(输出目录, { recursive: true });
    }
    
    const 读取流 = fs.createReadStream(压缩文件路径);
    const 解压器 = unzipper.ParseOne(目标文件, { path: 输出路径 });
    
    读取流
      .pipe(解压器)
      .on('close', () => resolve())
      .on('error', (错误) => reject(new Error(`解压特定文件失败: ${错误.message}`)));
  });
}

/**
 * 读取ZIP文件中的所有文件列表
 * @param {string} 压缩文件路径 - ZIP文件路径
 * @returns {Promise<Array>} 文件列表
 */
async function 获取ZIP文件列表(压缩文件路径) {
  验证Unzipper库();
  
  return new Promise((resolve, reject) => {
    if (!fs.existsSync(压缩文件路径)) {
      reject(new Error(`压缩文件不存在: ${压缩文件路径}`));
      return;
    }
    
    const 文件列表 = [];
    
    fs.createReadStream(压缩文件路径)
      .pipe(unzipper.Parse())
      .on('entry', (条目) => {
        文件列表.push({
          路径: 条目.path,
          类型: 条目.type, // 'Directory' 或 'File'
          大小: 条目.vars.uncompressedSize,
          修改时间: 条目.vars.lastModDate
        });
        条目.autodrain(); // 确保条目被正确处理
      })
      .on('close', () => resolve(文件列表))
      .on('error', (错误) => reject(new Error(`读取ZIP文件列表失败: ${错误.message}`)));
  });
}

/**
 * 从ZIP文件读取文件内容
 * @param {string} 压缩文件路径 - ZIP文件路径
 * @param {string} 目标文件 - 要读取的文件路径
 * @returns {Promise<Buffer>} 文件内容
 */
async function 从ZIP读取文件内容(压缩文件路径, 目标文件) {
  验证Unzipper库();
  
  return new Promise((resolve, reject) => {
    if (!fs.existsSync(压缩文件路径)) {
      reject(new Error(`压缩文件不存在: ${压缩文件路径}`));
      return;
    }
    
    const 数据块 = [];
    let 找到文件 = false;
    
    fs.createReadStream(压缩文件路径)
      .pipe(unzipper.Parse())
      .on('entry', (条目) => {
        if (条目.path === 目标文件) {
          找到文件 = true;
          条目.on('data', (数据) => {
            数据块.push(数据);
          });
          条目.on('end', () => {
            resolve(Buffer.concat(数据块));
          });
        } else {
          条目.autodrain();
        }
      })
      .on('close', () => {
        if (!找到文件) {
          reject(new Error(`在ZIP文件中未找到文件: ${目标文件}`));
        }
      })
      .on('error', (错误) => reject(new Error(`读取ZIP文件内容失败: ${错误.message}`)));
  });
}

/**
 * 流式解压ZIP文件
 * @param {Object} 输入流 - 输入流对象
 * @param {string} 目标目录 - 解压目标目录
 * @returns {Promise<void>}
 */
async function 流式解压ZIP(输入流, 目标目录) {
  验证Unzipper库();
  
  // 确保目标目录存在
  if (!fs.existsSync(目标目录)) {
    fs.mkdirSync(目标目录, { recursive: true });
  }
  
  return new Promise((resolve, reject) => {
    输入流
      .pipe(unzipper.Extract({ path: 目标目录 }))
      .on('close', () => resolve())
      .on('error', (错误) => reject(new Error(`流式解压ZIP失败: ${错误.message}`)));
  });
}

/**
 * 解压TAR文件（需要tar包）
 * @param {string} 压缩文件路径 - TAR文件路径
 * @param {string} 目标目录 - 解压目标目录
 * @returns {Promise<void>}
 */
async function 解压TAR文件(压缩文件路径, 目标目录) {
  try {
    const tar = require('tar');
    
    // 确保目标目录存在
    if (!fs.existsSync(目标目录)) {
      fs.mkdirSync(目标目录, { recursive: true });
    }
    
    if (!fs.existsSync(压缩文件路径)) {
      throw new Error(`压缩文件不存在: ${压缩文件路径}`);
    }
    
    await tar.x({ file: 压缩文件路径, cwd: 目标目录 });
  } catch (错误) {
    if (错误.code === 'MODULE_NOT_FOUND') {
      throw new Error('未安装 tar 库，请运行 "npm install tar" 来安装');
    }
    throw new Error(`解压TAR文件失败: ${错误.message}`);
  }
}

/**
 * 解压GZIP文件
 * @param {string} 压缩文件路径 - GZIP文件路径
 * @param {string} 输出文件路径 - 输出文件路径
 * @returns {Promise<void>}
 */
async function 解压GZIP文件(压缩文件路径, 输出文件路径) {
  const zlib = require('zlib');
  
  return new Promise((resolve, reject) => {
    if (!fs.existsSync(压缩文件路径)) {
      reject(new Error(`压缩文件不存在: ${压缩文件路径}`));
      return;
    }
    
    // 确保输出目录存在
    const 输出目录 = path.dirname(输出文件路径);
    if (!fs.existsSync(输出目录)) {
      fs.mkdirSync(输出目录, { recursive: true });
    }
    
    const 读取流 = fs.createReadStream(压缩文件路径);
    const 写入流 = fs.createWriteStream(输出文件路径);
    const 解压流 = zlib.createGunzip();
    
    读取流
      .pipe(解压流)
      .pipe(写入流)
      .on('close', () => resolve())
      .on('error', (错误) => reject(new Error(`解压GZIP文件失败: ${错误.message}`)));
  });
}

/**
 * 根据文件扩展名自动解压
 * @param {string} 压缩文件路径 - 压缩文件路径
 * @param {string} 目标目录 - 解压目标目录
 * @returns {Promise<void>}
 */
async function 自动解压(压缩文件路径, 目标目录) {
  if (!fs.existsSync(压缩文件路径)) {
    throw new Error(`压缩文件不存在: ${压缩文件路径}`);
  }
  
  const 文件扩展名 = path.extname(压缩文件路径).toLowerCase();
  
  if (文件扩展名 === '.zip') {
    return await 解压ZIP文件(压缩文件路径, 目标目录);
  } else if (文件扩展名 === '.tar') {
    return await 解压TAR文件(压缩文件路径, 目标目录);
  } else if (文件扩展名 === '.gz') {
    // 对于.gz文件，假设解压后使用原始文件名（去掉.gz后缀）
    const 输出文件名 = path.basename(压缩文件路径, '.gz');
    const 输出文件路径 = path.join(目标目录, 输出文件名);
    return await 解压GZIP文件(压缩文件路径, 输出文件路径);
  } else {
    throw new Error(`不支持的压缩文件格式: ${文件扩展名}`);
  }
}

// 导出API
module.exports = {
  // 压缩功能
  // 基础创建功能
  创建归档,
  创建ZIP文件,
  创建TAR文件,
  创建归档文件,
  
  // 文件和目录添加功能
  添加文件到归档,
  添加目录到归档,
  添加缓冲区到归档,
  添加文本到归档,
  
  // 流处理功能
  从流创建归档,
  流式输出归档到响应,
  
  // 高级压缩功能
  设置归档最大大小,
  监听归档进度,
  创建分割归档,
  获取归档统计,
  
  // 解压功能
  // 基本解压
  解压ZIP文件,
  解压TAR文件,
  解压GZIP文件,
  自动解压,
  
  // 高级解压
  解压特定文件,
  获取ZIP文件列表,
  从ZIP读取文件内容,
  流式解压ZIP,
  
  // 获取原始库引用
  获取原始archiver: () => archiver,
  获取原始unzipper: () => unzipper
};
